﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Minesweeper.Models
{
    public class Board
    {        
        protected Field[,] _fields;
        public Field[,] Fields { get { return _fields; } }

        protected int _countX;
        protected int _countY;

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="countX"></param>
        /// <param name="countY"></param>
        /// <param name="countBombs"></param>
        public Board(int countX, int countY, int countBombs)
        {
            this._countX = countX;
            this._countY = countY;
            this._fields = new Field[countX, countY];
            this._FillWithBombs(countBombs);
            this._FillOthers();
        }

        /// <summary>
        /// Füllt die restlichen Felder mit Zahlen oder Empty-Werten
        /// </summary>
        protected void _FillOthers()
        {
            for (int x = 0; x < this._countX; x++)
            {
                for (int y = 0; y < this._countY; y++)
                {
                    if (!isBomb(x,y)) {
                        int bombCount = this._getBombCountArroundField(x, y);
                        _fields[x,y] = new Field((Field.Types) bombCount);
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        protected int _getBombCountArroundField(int x, int y)
        {
            int bombCount = 0;
            int startX, startY, endX, endY;
            _getStartParams(x, y, out startX, out startY, out endX, out endY);
            

            for (int checkX = startX; checkX <= endX; checkX++)
            {
                for (int checkY = startY; checkY <= endY; checkY++)
                {
                    // Dürfen sich nicht auf dem Aktuellen Feld befinden
                    if (checkX != x || checkY != y)
                    {
                        // Wenn es Bombe ist, Counter erhöhen
                        if (isBomb(checkX, checkY)) 
                        {
                            bombCount++;
                        }
                    }
                }
            }
            return bombCount;
        }

        /// <summary>
        /// Füllt das Feld Random mit Bomben
        /// </summary>
        /// <param name="countBombs"></param>
        protected void _FillWithBombs(int countBombs)
        {
            Random random = new Random();
            while (countBombs > 0)
            {
                int x = random.Next(0, _countX);
                int y = random.Next(0, _countY);

                if (_fields[x, y] == null)
                {
                    _fields[x, y] = new Field(Field.Types.Bomb);
                    --countBombs;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="startX"></param>
        /// <param name="startY"></param>
        /// <param name="endX"></param>
        /// <param name="endY"></param>
        protected void _getStartParams(int x, int y, out int startX, out int startY, out int endX, out int endY)
        {
            if (x - 1 < 0)
                startX = 0;
            else
                startX = x - 1;

            if (y - 1 < 0)
                startY = 0;
            else
                startY = y - 1;

            if (x + 1 >= _countX)
                endX = x;
            else
                endX = x + 1;

            if (y + 1 >= _countY)
                endY = y;
            else
                endY = y + 1;
        }

        protected Boolean isBomb(int x, int y)
        {
            return _fields[x,y] != null && _fields[x,y].FieldType == Field.Types.Bomb;
        }

        /// <summary>
        /// Gibt eine Liste von Koordinaten zurück, bei die Leer sind und aufgedeckt werden müssen
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public List<Coordinate> getAffectedFieldsArround(List<Coordinate> coords, int x, int y)
        {
            int startX, startY, endX, endY;
            _getStartParams(x, y, out startX, out startY, out endX, out endY);

            Coordinate current = new Coordinate(x, y);
            if (!coords.Contains(current))
            {
                coords.Add(current);
            }
            
            for (int checkX = startX; checkX <= endX; checkX++)
            {
                for (int checkY = startY; checkY <= endY; checkY++)
                {
                    // Dürfen sich nicht auf dem Aktuellen Feld befinden
                    if (checkX != x || checkY != y)
                    {
                        Coordinate currentCheck = new Coordinate(checkX, checkY);
                        if (!coords.Contains(currentCheck))
                        {
                            if (_fields[checkX, checkY].FieldType == Field.Types.Empty)
                            {
                                List<Coordinate> coordsNew = getAffectedFieldsArround(coords, checkX, checkY);
                                coords.Concat(coordsNew);
                            }
                            else
                            {
                                coords.Add(currentCheck);
                            }
                        }
                    }
                }
            }    

            return coords;
        }
    }
}
